/***********************************************************************************************************************
* Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 
* the License. You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file        opencc_utf8.c
*
* @brief       Other character coding types converte to UTF-8 coding or 
*              UTF-8 encoding converts to other character coding types.
* @revision
* Date         Author          Notes
* 2020-11-20   OneOS Team      First Version
***********************************************************************************************************************
*/


#include "opencc_utf8.h"


static int cc_utf8_get_bytes(unsigned char ch)
{
    int byte_num = 0;

    if (ch >= 0xFC && ch < 0xFE)
    {
        byte_num = 6;
    }
    else if (ch >= 0xF8) 
    {
        byte_num = 5;
    }
    else if (ch >= 0xF0)
    {
        byte_num = 4;
    }
    else if (ch >= 0xE0)
    {
        byte_num = 3;
    }
    else if (ch >= 0xC0)
    {
        byte_num = 2;
    }
    else if (0 == (ch & 0x80))
    {   
        byte_num = 1;
    }
    
    return byte_num;
}

/**
 * 将一个字符的Unicode(UCS-2和UCS-4)编码转换成UTF-8编码.
 */
static int cc_utf8_from_unicode(unsigned long unicode, 
                                        void *utf8_buf, unsigned int size)
{
    unsigned char *pout = (unsigned char *)utf8_buf;


    if ((unicode <= 0x0000007f) && size >= 1)
    {
        /* 0xxxxxxx */
        *pout = (unsigned char)(unicode & 0x7f);
        return 1;
    }
    else if ((unicode >= 0x00000080 && unicode <= 0x000007FF) && size >= 2)
    {
        /* 110xxxxx 10xxxxxx */
        *pout++ = ((unicode >> 6) & 0x1f) | 0xc0;
        *pout++ = (unicode & 0x3f) | 0x80;
        return 2;
    }
    else if ((unicode >= 0x00000800 && unicode <= 0x0000FFFF) && size >= 3)
    {
        /* 1110xxxx 10xxxxxx 10xxxxxx */
        *pout++ = ((unicode >> 12) & 0x0f) | 0xe0;
        *pout++ = ((unicode >> 6) & 0x3f) | 0x80;
        *pout++ = (unicode & 0x3f) | 0x80;
        return 3;
    }
    else if ((unicode >= 0x00010000 && unicode <= 0x001FFFFF) && size >= 4)
    {
        /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */  
        *pout++ = ((unicode >> 18) & 0x07) | 0xf0;
        *pout++ = ((unicode >> 12) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 6) & 0x3f) | 0x80;
        *pout++ = (unicode & 0x3f) | 0x80;
        return 4;
    }
    else if ((unicode >= 0x00200000 && unicode <= 0x03FFFFFF) && size >= 5)
    {
        /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
        *pout++ = ((unicode >> 24) & 0x03) | 0xf8;
        *pout++ = ((unicode >> 18) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 12) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 6) & 0x3f) | 0x80;
        *pout++ = (unicode & 0x3f) | 0x80;
        return 5;
    }
    else if ((unicode >= 0x04000000 && unicode <= 0x7FFFFFFF) && size >= 6)
    {
        /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
        *pout++ = ((unicode >> 30) & 0x01) | 0xfc;
        *pout++ = ((unicode >> 24) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 18) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 12) & 0x3f) | 0x80;
        *pout++ = ((unicode >> 6) & 0x3f) | 0x80;
        *pout++ = (unicode & 0x3f) | 0x80;
        return 6;
    }
  
    return -1;
}

static int cc_utf8_result_is_invalid(unsigned long *unicode)
{
     /* 从下一次字节再开始转unicode */
    *unicode = 0xFFFFFFFF;
    return 1;
}

/**
 * 将一个字符的UTF8编码转换成Unicode(UCS-2和UCS-4)编码.
 */
static int cc_utf8_to_unicode(const void *utf8_buf, unsigned size, 
                                            unsigned long *unicode)
{
    /* |--------utf8_buf-------| */
    /* |..|..|b6|b5|b4|b3|b2|b1| */
    char b1, b2, b3, b4, b5, b6;
    unsigned char *pinput = (unsigned char *)utf8_buf;
    int utfbytes = cc_utf8_get_bytes(*pinput);

    //LOG_D("%s, %d, utfbytes %d.", __func__, __LINE__, utfbytes);

    if (size < utfbytes)
    {
        /* size与实际size不符时，返回unicode 0，停止取值。 */
        *unicode = 0;
        return size;
    }
    
    *unicode = 0x0; 
    switch (utfbytes)
    {
        case 1:
            *unicode = *pinput;
            break;
        case 2:
            b1 = *pinput;
            b2 = *(pinput + 1);
            if ((b2 & 0xe0) != 0x80)
            {
                return cc_utf8_result_is_invalid(unicode);
            }
            *unicode = ((b1 & 0x1f) << 6) | (b2 & 0x3f);
            break;
        case 3:
            b1 = *pinput;
            b2 = *(pinput + 1);
            b3 = *(pinput + 2);
            if (((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) )
            {
                return cc_utf8_result_is_invalid(unicode);
            }
            *unicode = ((b1 & 0xf) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f);
            break;
        case 4:
            b1 = *pinput;
            b2 = *(pinput + 1);
            b3 = *(pinput + 2);
            b4 = *(pinput + 3);
            if (((b2 & 0xc0) != 0x80) || 
                ((b3 & 0xc0) != 0x80) || 
                ((b4 & 0xc0) != 0x80))
            {
                return cc_utf8_result_is_invalid(unicode);
            }
            *unicode = ((b1 & 0x7) << 18) | ((b2 & 0x3f) << 12) | 
                        ((b3 & 0x3f) << 6) | (b4 & 0x3f);
            break;
        case 5:
            b1 = *pinput;
            b2 = *(pinput + 1);
            b3 = *(pinput + 2);
            b4 = *(pinput + 3);
            b5 = *(pinput + 4);
            if (((b2 & 0xc0) != 0x80) || ((b3 & 0xc0) != 0x80)
                    || ((b4 & 0xc0) != 0x80) || ((b5 & 0xc0) != 0x80))
            {
                return cc_utf8_result_is_invalid(unicode);
            }
            *unicode = ((b1 & 0x3) << 24) | ((b2 & 0x3f) << 18) | 
                       ((b3 & 0x3f) << 12) | ((b4 & 0x3f) << 6) | (b5 & 0x3f);
            break;
        case 6:
            b1 = *pinput;
            b2 = *(pinput + 1);
            b3 = *(pinput + 2);
            b4 = *(pinput + 3);
            b5 = *(pinput + 4);
            b6 = *(pinput + 5);
            if (((b2 & 0xc0) != 0x80) || ((b3 & 0xc0) != 0x80)
                    || ((b4 & 0xc0) != 0x80) || ((b5 & 0xc0) != 0x80)
                    || ((b6 & 0xc0) != 0x80))
            {
                return cc_utf8_result_is_invalid(unicode);
            }
            *unicode = ((b1 & 0x3) << 30) | ((b2 & 0x3f) << 24) | ((b3 & 0x3f) << 18) | 
						((b4 & 0x3) << 12)| ((b5 & 0x3f) << 6) | (b6 & 0x3f);
            break;
            
        default:
            return cc_utf8_result_is_invalid(unicode);
    }
    return utfbytes;
}

static cc_charc_set_t cc_utf8_set_entry = {
    .charc_type = CC_CHARC_TYPE_UTF8,
    .min = 1,
    .max = 6,
    .unknown_char = '?',
    .charc_set_to_unicode = cc_utf8_to_unicode,
    .unicode_to_charc_set = cc_utf8_from_unicode,
};

cc_charc_set_t *cc_utf8_get_entry(void)
{
    return &cc_utf8_set_entry;
}



